package cn.itcast.nems.oldems.service.sync.impl;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.lang.Assert;
import cn.itcast.ic.common.exception.v2.BizExceptionProducer;
import cn.itcast.nems.approval.service.ApprovalFormService;
import cn.itcast.nems.common.account.AccountHolder;
import cn.itcast.nems.common.account.AccountInfo;
import cn.itcast.nems.manager.ems.EmsManger;
import cn.itcast.nems.manager.support.ApprovalMessageHolder;
import cn.itcast.nems.manager.support.ApprovalMessageInfo;
import cn.itcast.nems.manager.util.CustomBeanUtil;
import cn.itcast.nems.manager.util.WeixinMessageUtil;
import cn.itcast.nems.manager.weixin.WeixinManager;
import cn.itcast.nems.oldems.constant.FinanceCorporationEnum;
import cn.itcast.nems.oldems.converter.StudentJoinClassCirculationConverter;
import cn.itcast.nems.oldems.dao.mapper.StudentJoinClassMapper;
import cn.itcast.nems.oldems.dto.MergeStudentReason;
import cn.itcast.nems.oldems.dto.Refund;
import cn.itcast.nems.oldems.dto.StudentRecordCirculation;
import cn.itcast.nems.oldems.dto.oldapi.EmsApiResult;
import cn.itcast.nems.oldems.dto.oldapi.StudentJoinClassCirculation;
import cn.itcast.nems.oldems.service.sync.OldEmsSyncConfig;
import cn.itcast.nems.oldems.service.sync.OldEmsSyncService;
import cn.itcast.nems.studentrecord.constant.OrderSourceEnum;
import cn.itcast.nems.studentrecord.constant.RefundTypeEnum;
import cn.itcast.nems.studentrecord.dao.entity.StudentRecord;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordFundFlowDTO;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordFundFlowInfoDTO;
import cn.itcast.nems.studentrecord.enumeration.StudentRecordErrorBodyEnum;
import cn.itcast.nems.studentrecord.service.studentrecord.StudentRecordService;
import cn.itcast.nems.studentrecord.service.studentrecord.StudentRecordStudentRelationshipService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Supplier;

/**
 * author liyong
 * create: 2024-03-13 18:17:20
 **/
@Service
public class OldEmsSyncServiceImpl implements OldEmsSyncService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final StudentJoinClassMapper studentJoinClassMapper;
    private final EmsManger emsManger;
    private final StudentRecordService studentRecordService;
    private final StudentRecordStudentRelationshipService studentRecordStudentRelationshipService;
    private final ApprovalFormService approvalFormService;
    private final OldEmsSyncConfig syncConfig;
    private final WeixinManager weixinManager;

    public OldEmsSyncServiceImpl(StudentJoinClassMapper studentJoinClassMapper,
                                 EmsManger emsManger,
                                 StudentRecordService studentRecordService,
                                 StudentRecordStudentRelationshipService studentRecordStudentRelationshipService,
                                 ApprovalFormService approvalFormService,
                                 OldEmsSyncConfig syncConfig,
                                 WeixinManager weixinManager) {
        this.studentJoinClassMapper = studentJoinClassMapper;
        this.emsManger = emsManger;
        this.studentRecordService = studentRecordService;
        this.studentRecordStudentRelationshipService = studentRecordStudentRelationshipService;
        this.approvalFormService = approvalFormService;
        this.syncConfig = syncConfig;
        this.weixinManager = weixinManager;
    }

    @Override
    public EmsApiResult circulations(List<StudentRecordCirculation> studentRecordCirculations) {
        return requestOldEms(() -> {
            if (studentRecordCirculations != null && !studentRecordCirculations.isEmpty()) {
                List<StudentJoinClassCirculation> sjcCirculations = new ArrayList<>();
                for (StudentRecordCirculation c : studentRecordCirculations) {
                    final int studentId = c.getStudentId();
                    final Integer clazzId = c.getOriginStudentJoinClassInfo().getClazzId();
                    final Integer orgiginSjcId = this.studentJoinClassMapper.selectStudentJoinClassId(studentId, clazzId);
                    Assert.notNull(orgiginSjcId, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.BAD_REQUEST, "该学籍在老EMS不存在！"));
                    final StudentJoinClassCirculation sjcCirculation = StudentJoinClassCirculationConverter.convert(orgiginSjcId, c);
                    //if (this.syncConfig.isIgnoredSjcId(orgiginSjcId)) {
                    sjcCirculation.setIgnoredCheck(true);
                    //}
                    sjcCirculations.add(sjcCirculation);
                }
                final String jsonString = JSON.toJSONString(sjcCirculations);
                logger.info("batchClassCirculation reqeust:{}", jsonString);
                return this.emsManger.batchClassCirculation(jsonString);
            }
            return null;
        });
    }

    @Override
    public EmsApiResult suspension(Integer studentId, Integer clazzId, LocalDate suspendDate, String operatorEmail) {
        return requestOldEms(() -> {
            final Integer joinclassid = this.studentJoinClassMapper.selectStudentJoinClassId(studentId, clazzId);
            logger.info("休学 studentId:{} clazzId:{} LocalDate:{}, operatorEmail{}", studentId, clazzId, suspendDate, operatorEmail);
            Assert.notNull(joinclassid, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.BAD_REQUEST, "该学籍在老EMS不存在！"));
            return this.emsManger.suspension(joinclassid, suspendDate, operatorEmail);
        });
    }

    @Override
    public EmsApiResult refund(Refund refund, String loginUser) {
        logger.info("调用老EMS接口 同步【退费单】或【退费退回】， 请求参数：{}, loginUser：{}", JSON.toJSONString(refund, SerializerFeature.DisableCircularReferenceDetect), loginUser);
        return requestOldEms(() -> {
            String studentRecordId = refund.getJoinclassid();
            StudentRecord studentRecord = studentRecordService.getById(studentRecordId);
            Assert.notNull(studentRecord, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.BAD_REQUEST, "该学籍不存在！"));
            Integer clazzId = studentRecord.getClazzId();
            Integer studentId = studentRecordStudentRelationshipService.findStudentId(studentRecordId);
            final Integer joinclassid = this.studentJoinClassMapper.selectStudentJoinClassId(studentId, clazzId);
            Assert.notNull(joinclassid, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.BAD_REQUEST, "该学籍在老EMS不存在！"));
            refund.setJoinclassid(String.valueOf(joinclassid));
            final String jsonParams = JSON.toJSONString(refund, CustomBeanUtil.CAMEL_CASE_CONFIG);
            final Map<String, Object> params = JSON.parseObject(jsonParams, Map.class);
            return this.emsManger.refund(params, loginUser);
        });
    }

    @Override
    public EmsApiResult dropout(String studentRecordId, LocalDate bizDate, String type, String loginUser) {
        return requestOldEms(() -> {
            StudentRecord studentRecord = studentRecordService.getById(studentRecordId);
            Assert.notNull(studentRecord, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.BAD_REQUEST, "该学籍不存在！"));
            Integer studentId = studentRecordStudentRelationshipService.findStudentId(studentRecordId);
            Integer clazzId = studentRecord.getClazzId();
            if (studentId != null && clazzId != null) {
                TreeMap<String, Object> requestJson = new TreeMap<>();
                requestJson.put("classid", clazzId);
                requestJson.put("studentid", studentId);
                if (bizDate != null) {
                    requestJson.put("leaveschoolDate", bizDate);
                }
                if (RefundTypeEnum.DROPOUT_REFUND.name().equals(type)) {
                    requestJson.put("leaveSchoolRefundType", 1);
                } else if (RefundTypeEnum.DROPOUT.name().equals(type)) {
                    requestJson.put("leaveSchoolRefundType", 0);
                }
                return this.emsManger.dropout(requestJson, loginUser);
            }
            return null;
        });
    }

    @Override
    public EmsApiResult fundTransfer(String originStudentRecordId, String targetStudentRecordId, Boolean originChannelType, String paychannel, String type, String loginUser, String originChannelCode, Integer transferAmount) {
        return requestOldEms(() -> {
            final Integer orgiginSjcId = getOldEmsJoinclasslId(originStudentRecordId);
            final Integer targetSjcId = getOldEmsJoinclasslId(targetStudentRecordId);
            if (orgiginSjcId == targetSjcId) {
                EmsApiResult success = EmsApiResult.SUCCESS;
                return JSON.toJSONString(success);
            }
            TreeMap<String, Object> requestJson = new TreeMap<>();
            requestJson.put("id", orgiginSjcId);
            if (StringUtils.hasText(originChannelCode)) {
                requestJson.put("originChannelId", originChannelCode);
            } else {
                requestJson.put("originChannelId", paychannel);
            }
            requestJson.put("toid", targetSjcId);
            if (originChannelType != null) {
                requestJson.put("originChannelType", originChannelType);
            }
            if (StringUtils.hasText(paychannel)) {
                requestJson.put("paychannel", paychannel);
            }
            requestJson.put("type", type);
            requestJson.put("feeType", 2);
            requestJson.put("fee", BigDecimal.valueOf(transferAmount).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP));
            return this.emsManger.fundTransfer(requestJson, loginUser);
        });
    }

    private Integer getOldEmsJoinclasslId(String studentRecordId) {
        StudentRecord studentRecord = studentRecordService.getById(studentRecordId);
        Assert.notNull(studentRecord, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.BAD_REQUEST, "NEMS学籍不存在！"));
        Integer studentId = null;
        Integer clazzId = null;
        if (OrderSourceEnum.SUSPENSION.name().equalsIgnoreCase(studentRecord.getSource())) {
            if (StringUtils.hasText(studentRecord.getAssociatedRecordId())) {
                studentRecord = studentRecordService.getById(studentRecord.getAssociatedRecordId());
                Assert.notNull(studentRecord, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.BAD_REQUEST, "NEMS上一个学籍ID不存在！"));
                clazzId = studentRecord.getClazzId();
                studentId = studentRecordStudentRelationshipService.findStudentId(studentRecord.getId());
            } else {
                clazzId = studentRecord.getClazzId();
                studentId = studentRecordStudentRelationshipService.findStudentId(studentRecordId);
            }
        } else {
            clazzId = studentRecord.getClazzId();
            studentId = studentRecordStudentRelationshipService.findStudentId(studentRecordId);
        }
        final Integer targetSjcId = this.studentJoinClassMapper.selectStudentJoinClassId(studentId, clazzId);
        Assert.notNull(targetSjcId, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.BAD_REQUEST, "老入班学籍不存在！"));
        return targetSjcId;
    }

    @Override
    public EmsApiResult fundTransferByTransfer(
            String originStudentRecordId, String targetStudentRecordId,
            Boolean originChannelType, String paychannel,
            String type, String loginUser,
            String originChannelCode,
            Integer transferAmount
    ) {
        return requestOldEms(() -> {
            final Integer orgiginSjcId = getOldEmsJoinclasslIdByTransfer(originStudentRecordId);
            final Integer targetSjcId = getOldEmsJoinclasslIdByTransfer(targetStudentRecordId);
            if (orgiginSjcId == targetSjcId) {
                EmsApiResult success = EmsApiResult.SUCCESS;
                return JSON.toJSONString(success);
            }
            TreeMap<String, Object> requestJson = new TreeMap<>();
            requestJson.put("id", orgiginSjcId);
            if (StringUtils.hasText(originChannelCode)) {
                requestJson.put("originChannelId", originChannelCode);
            } else {
                requestJson.put("originChannelId", paychannel);
            }
            requestJson.put("toid", targetSjcId);
            if (originChannelType != null) {
                requestJson.put("originChannelType", originChannelType);
            }
            if (StringUtils.hasText(paychannel)) {
                requestJson.put("paychannel", paychannel);
            }
            requestJson.put("type", type);
            requestJson.put("feeType", 2);
            requestJson.put("fee", BigDecimal.valueOf(transferAmount).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP));
            return this.emsManger.fundTransfer(requestJson, loginUser);
        });
    }

    private Integer getOldEmsJoinclasslIdByTransfer(String studentRecordId) {
        StudentRecord studentRecord = studentRecordService.getById(studentRecordId);
        Assert.notNull(studentRecord, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.BAD_REQUEST, "NEMS学籍不存在！" + studentRecordId));
        Integer studentId = null;
        Integer clazzId = null;
        clazzId = studentRecord.getClazzId();
        studentId = studentRecordStudentRelationshipService.findStudentId(studentRecordId);
        final Integer targetSjcId = this.studentJoinClassMapper.selectStudentJoinClassId(studentId, clazzId);
        Assert.notNull(targetSjcId, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.BAD_REQUEST, "OEMS学籍不存在！" + studentRecordId));
        return targetSjcId;
    }

    @Override
    public EmsApiResult fundFlow(StudentRecordFundFlowInfoDTO studentRecordFundFlowInfoDO, StudentRecordFundFlowDTO result, BigDecimal amount, String schoolCode, String paymentAccountCode, LocalDateTime dateTime, BigDecimal charge) {
        return requestOldEms(() -> {
            String studentRecordId = result.getStudentRecordId();
            final Integer orgiginSjcId = getOldEmsJoinclasslId(studentRecordId);
            TreeMap params = new TreeMap<>();
            params.put("joinclassid", orgiginSjcId);
            if (StringUtils.hasText(result.getRemark())) {
                params.put("name", result.getRemark());
            } else {
                params.put("name", "补录流水");
            }
            params.put("cost", amount);
            params.put("incomeExpensestype", 1);
            if (StringUtils.hasText(result.getPaymentNo())) {
                params.put("acquittanceno", result.getPaymentNo());
            }
            if (StringUtils.hasText(result.getPaymentNo())) {
                params.put("voucherno", result.getPaymentNo());
            }
            params.put("schoolCode", schoolCode);
            params.put("paymentchannel", paymentAccountCode);
            params.put("financetime", dateTime);
            String financeCorporationName = studentRecordFundFlowInfoDO.getFinanceCorporationName();
            if (StringUtils.hasText(financeCorporationName)) {
                FinanceCorporationEnum financeCorporationEnum = FinanceCorporationEnum.findValue(financeCorporationName);
                params.put("financeCorporation", financeCorporationEnum.name());
            }
            if (charge != null) {
                params.put("commissionRate", charge);
            }
            if (StringUtils.hasText(studentRecordFundFlowInfoDO.getPosNo())) {
                params.put("posNo", studentRecordFundFlowInfoDO.getPosNo());
            }
            return this.emsManger.fundFlow(params, AccountHolder.getRequiredAccountInfo().getEmail());
        });
    }

    @Override
    public EmsApiResult suspensionReturn(Integer sjcId, String loginUser) {
        return requestOldEms(() -> this.emsManger.suspensionReturn(sjcId, loginUser));
    }

    @Override
    public Integer checkAndGetOldEmsSJcId(@NonNull String studentRecordId) {
        if (!this.syncConfig.isSysnc()) {
            return null;
        }
        StudentRecord studentRecord = studentRecordService.getById(studentRecordId);
        Assert.notNull(studentRecord, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.BAD_REQUEST, "该学籍不存在！"));
        Integer studentId = studentRecordStudentRelationshipService.findStudentId(studentRecordId);
        Integer clazzId = studentRecord.getClazzId();
        final Integer oldEmsSjcId = this.studentJoinClassMapper.selectStudentJoinClassId(studentId, clazzId);
        Assert.notNull(oldEmsSjcId, BizExceptionProducer.produce(StudentRecordErrorBodyEnum.BAD_REQUEST, "该学籍在老EMS不存在！"));
        return oldEmsSjcId;
    }

    @Override
    public EmsApiResult adjustPrice(@NonNull String studentRecordId, @NonNull Integer price, @NonNull String loginUser) {
        return requestOldEms(() -> {
            BigDecimal factor = new BigDecimal("100");
            final Integer oldEmsSjcId = this.checkAndGetOldEmsSJcId(studentRecordId);
            return this.emsManger.adjustPrice(oldEmsSjcId, BigDecimal.valueOf(price).divide(factor, 2, RoundingMode.HALF_UP), loginUser);
        });
    }

    @Override
    public EmsApiResult mergeStudent(Integer fromStudentId, Integer toStudentId, MergeStudentReason reason, String operatorEmail) {
        return requestOldEms(() -> this.emsManger.mergeStudent(fromStudentId, toStudentId, reason.getDesc(), operatorEmail));
    }

    @Override
    public boolean isSyncOn() {
        return this.syncConfig.isSysnc();
    }

    private EmsApiResult requestOldEms(Supplier<String> supplier) {
        if (this.syncConfig.isSysnc()) {
            try {
                final String response = supplier.get();
                logger.info("old ems response:{}", response);
                if (StringUtils.hasText(response)) {
                    final JSONObject jsonObject = JSON.parseObject(response);
                    EmsApiResult result = JSON.toJavaObject(jsonObject, EmsApiResult.class);
                    if (!result.isSuccess()) {
                        final String errorMessage = result.getErrorMessage();
                        throw new RuntimeException("老EMS返回错误：" + errorMessage);
                    }
                    return result;
                }
            } catch (Exception e) {
                // throw new RuntimeException("请求老EMS错误：" + e.getMessage());
                this.processException(e);
            }
        } else {
            logger.info("同步到老EMS功能已关闭");
        }
        return EmsApiResult.SUCCESS;
    }

    private void processException(Exception e) {
        String msg = ExceptionUtil.getRootCauseMessage(e);
        final ApprovalMessageInfo info = ApprovalMessageHolder.get();
        String userName = "";
        String userEmail = "";
        final Optional<AccountInfo> accountInfo = AccountHolder.getAccountInfo();
        if (accountInfo.isPresent()) {
            final AccountInfo acc = accountInfo.get();
            userName = acc.getName();
            userEmail = acc.getEmail();
        }
        final String wxMessage = WeixinMessageUtil.createWeixinMessage(info.journalId(),
                info.journalType() + " - 同步老EMS失败",
                info.bpmBizKey(),
                info.msgId(),
                userName,
                userEmail,
                info.envProfile(),
                e);
        this.weixinManager.sendMarkdownMessage(info.msgId(), wxMessage);
        final String approvalFormId = info.approvalFormId();
        logger.warn("同步老EMS错误: {}, {}", e.getMessage(), approvalFormId, e);
        if (StringUtils.hasText(approvalFormId)) {
            approvalFormService.updateDescription(approvalFormId, msg);
        }
    }
}
